package ace.module.weixin.core.impl.manager.impl;

import ace.cmp.core.constants.CoreConstant;
import ace.cmp.core.utils.SystemClockUtils;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.spring.AbstractSpringDtoManager;
import ace.cmp.id.generator.api.LongIdGenerator;
import ace.cmp.json.api.JsonService;
import ace.module.weixin.api.model.dto.WxsCacheDto;
import ace.module.weixin.core.impl.dao.entity.WxsCache;
import ace.module.weixin.core.impl.dao.repository.WxsCacheRepository;
import ace.module.weixin.core.impl.manager.WxsCacheManager;
import ace.module.weixin.core.impl.manager.cache.WxsCacheCacheManager;
import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 微信缓存数据,保存accessToken、ticket等有时效数据 spring data jdbc querydsl rsql manager implement.
 *
 * @author Caspar
 */
@Slf4j
@AllArgsConstructor
@Component
public class WxsCacheManagerImpl
    extends AbstractSpringDtoManager<WxsCache, Long, WxsCacheDto>
    implements WxsCacheManager {

  private final WxsCacheRepository wxsCacheRepository;
  private final WxsCacheCacheManager wxsCacheCacheManager;
  private final JsonService jsonService;
  private final LongIdGenerator idGenerator;

  @Override
  public Optional<WxsCache> getByKeyFromDbOrCache(String key) {
    Optional<WxsCache> wxsCacheOptional = Optional.ofNullable(
        wxsCacheCacheManager.getByKeyAndDeleteFlagIsNot(key)
    );
    if (wxsCacheOptional.isEmpty()) {
      return Optional.empty();
    }
    if (SystemClockUtils.currentTimeMillis() > wxsCacheOptional.get().getExpireTime()) {
      return Optional.empty();
    }
    return wxsCacheOptional;
  }

  @Override
  public void removeDbAndCacheByKey(String key) {
    wxsCacheCacheManager.removeByKey(key);
  }

  @Override
  public void saveOrUpdate(String key, long expireInMills, Object value) {
    Optional<WxsCache> wxsCacheOptional = this.getByKeyFromDbOrCache(key);
    if (wxsCacheOptional.isEmpty()) {
      String jsonValue = this.jsonService.toJson(value);
      WxsCache wxsCache = WxsCache.builder()
          .id(idGenerator.get())
          .key(key)
          .value(jsonValue)
          .expireTime(expireInMills)

          .createTime(Instant.now().toEpochMilli())
          .updateTime(Instant.now().toEpochMilli())
          .rowVersion(CoreConstant.DEFAULT_ROW_VERSION)
          .build();

      this.wxsCacheRepository.insert(wxsCache);
    } else {
      WxsCache wxsCache = wxsCacheOptional.get();
      String jsonValue = this.jsonService.toJson(value);
      wxsCache.setValue(jsonValue);
      wxsCache.setExpireTime(expireInMills);
      wxsCache.setUpdateTime(Instant.now().toEpochMilli());

      this.wxsCacheCacheManager.updateById(wxsCache);
    }
  }

  @Override
  public <T> Optional<T> getValueByKeyFromDbOrCache(String key, Class<T> cls) {
    return this.getByKeyFromDbOrCache(key).map(wxsCache -> this.toValue(wxsCache, cls));
  }

  private <T> T toValue(WxsCache wxsCache, Class<T> cls) {
    if (Objects.isNull(wxsCache)) {
      return null;
    }
    return this.jsonService.toObject(wxsCache.getValue(), cls);
  }
}
